﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace md5
{
    class Program
    {
        private static HashAlgorithm md5 = HashAlgorithm.Create("MD5");
        
        static int Main(string[] args)
        {
            if (args.Length != 2)
            {
                Console.WriteLine("md5.exe create|verify <file name>");
                return 1;
            }

            bool isCreate = args[0].ToLower() == "create";
            string fileName = args[1];
            string md5Name = fileName + ".md5";
            if (!File.Exists(fileName))
            {
                WriteError(string.Format("File {0} not found", new FileInfo(fileName)));
                return 2;
            }

            try
            {
                if (isCreate)
                {
                    using (FileStream fs = new FileStream(md5Name, FileMode.Create, FileAccess.Write, FileShare.Read))
                    using (StreamWriter wr = new StreamWriter(fs, Encoding.ASCII))
                    {
                        string hash = CalcHash(fileName);
                        wr.Write(hash);
                        Console.WriteLine(hash);
                    }
                }
                else
                {
                    if (!File.Exists(md5Name))
                    {
                        WriteError(string.Format("MD5 of file {0} not found", new FileInfo(fileName)));
                        return 3;
                    }

                    var other = File.ReadAllText(md5Name, Encoding.ASCII).Trim(' ', '\t', '\r', '\n');
                    var real = CalcHash(fileName);
                    bool isOk = StringComparer.InvariantCultureIgnoreCase.Compare(other, real) == 0;
                    if (!isOk)
                    {
                        WriteError("Hash is not equal");
                        return 4;
                    }
                    else
                    {
                        Console.WriteLine("MD5 OK: " + real);
                        return 0;
                    }
                }

            }
            catch (Exception ex)
            {
                WriteError(ex.ToString());
                return 5;
            }

            return -1;
        }

        static void WriteError(string msg)
        {
            var c = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.DarkRed;
            Console.Error.WriteLine(msg);
            Console.ForegroundColor = c;
        }

        static string CalcHash(string fileName)
        {
            using(FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                var ret = md5.ComputeHash(fs);
                return HashAsString(ret);
            }
        }

        static string HashAsString(byte[] arg)
        {
            return string.Join("", arg.Select(x => x.ToString("X2")).ToArray());
        }
    }
}
